﻿using System;
using System.Collections.Generic;
using System.Text;
using CodeDynamiksEngine;
using CodeDynamiksCtl;
using MESCodeFile;

namespace LiveNotificationSystem
{
    
    public class PushNotification
    {
        
        CodeProcessor mainProc = new CodeProcessor();
        List<string> msgType = new List<string>();
        System.Threading.Timer _procTmr;
        string sysMsg = "Connecting to the Notification Service...";
        bool hasErr = false;
        int tmrInt = 1500;
        bool allowLoadNotification = true;
        public NotificationSystem NotificationSys = new NotificationSystem();
        public PushNotification()
        {
            Events();
            InitScript();
        }
        ~PushNotification()
        {
            _procTmr = null;
        }

        void ProcTimer(object tmr)
        {
            if (allowLoadNotification)
            {
                allowLoadNotification = false;
                LoadNotificationScript();
            }
        }
        void Events()
        {
            mainProc.ProcessCompleted += new EventHandler(delegate(object s, EventArgs e)
            {
                    DoMainProc();
            });
        }
        void DoMainProc()
        {
            if (mainProc.State == ProcessorFlag.DataRdyErr ||
                mainProc.State == ProcessorFlag.ReadyErr ||
                mainProc.State == ProcessorFlag.Error)
            {
                //Possible error is that the mainProc did not find the tables or
                //it cannot connect to the database server.
                mainProc.SafeCall(Common.PARENT, delegate
                {
                    NotificationSys.PullAll();
                    sysMsg = "Connecting to the Notification Service...";
                    hasErr = true;
                    if (NotificationUpdated != null) NotificationUpdated(this, new EventArgs());
                });
                mainProc.ClearErrorFlag();
                allowLoadNotification = true;
                return;
            }
            switch (mainProc.GetLastProcessName())
            {
                case "INITPROC":
                    LoadNotificationScript();
                    break;
                case "LOADNOTIFICATION":
                    FetchNotifications();
                    break;
            }
        }
        void InitScript()
        {

            //Use this script to instantiate pre-defined programmable cores.
            CodeScript s = new CodeScript("INITPROC");

            //DB Unit
            s.Add(CodeIns.DBU_NEW_CORE);
            s.Add(CodeIns.DBU_END);
            
            mainProc.Run(s);
            //Instantiate the timer
            _procTmr = new System.Threading.Timer(ProcTimer, null, 0, System.Threading.Timeout.Infinite);
            if (Common.CFG.CheckKey("NOTIFY_INTERVAL")) tmrInt = int.Parse(Common.CFG.Read("NOTIFY_INTERVAL").ToString());
            
            _procTmr.Change(0, tmrInt);
            
        }
        void LoadNotificationScript()
        {
            if (mainProc.State == ProcessorFlag.Running)
            {
                allowLoadNotification = true;
                return;
            }
            try
            {
                CodeScript s = new CodeScript("LOADNOTIFICATION");
                s.Add(CodeIns.SYS_USE_CORE, 0);
                s.Add(CodeIns.DBU_CONNECT, Common.CFG.Read("DRIVER"), Common.CFG.Read("DB"),
                        Common.CFG.Read("UID"), Common.CFG.Read("PWD"), Common.CFG.Read("SERVER"));
                s.Add(CodeIns.DBU_BEGIN_TRANSACTION);
                //Fetch the valid privileges.
                s.Add(CodeIns.DBU_QUERY, "SELECT fData FROM tbllookup WHERE fKey='PRIVILEGE' ORDER BY fData ASC");

                //Fetch notification logic
                string privsql = "(";
                for (int i = 0; i < CurrentUser.PRIV.Length; i++)
                {
                    privsql += "fMsgType='" + CurrentUser.PRIV[i] + "'";
                    if (i < CurrentUser.PRIV.Length - 1) privsql += " OR ";
                }
                if (privsql != "(") privsql += ") AND (fStatus='PUSHED')"; 
                s.Add(CodeIns.DBU_QUERY, "SELECT fID, fStamp, fMsgType, fMsg, fMeta, fStatus, fUID FROM tblnotificationservice WHERE " + privsql);
                s.Add(CodeIns.DBU_END);
                if (mainProc.State == ProcessorFlag.Running)
                {
                    allowLoadNotification = true;
                    return;
                }
                mainProc.Run(s);
            }
            catch (Exception)
            {
                //The most possible error to occur is that settings are not read properly.
                //Second is that the mainProc encountered an unrecoverable problem.
                mainProc.SafeCall(Common.PARENT, delegate
                {
                    NotificationSys.PullAll();
                    sysMsg = "Connecting to the Notification Service...";
                    hasErr = true;
                    if (NotificationUpdated != null) NotificationUpdated(this, new EventArgs());
                    allowLoadNotification = true;
                });
                
            }
        }
        void FetchNotifications()
        {
            //Fetch the ntoification from service
            DataCache c;
            mainProc.DataFetch(out c);
            //If the result of fetch does not match the number of expected result for whatever reason, return to calling code.
            if (c.GetPageSize() != 2) return;
            //Get the valid privileges.
            c.SetActivePage(0);
            msgType = new List<string>();
            msgType.AddRange(c.GetStringList("fData"));
            //Get the available notifications.
            c.SetActivePage(1);    
            mainProc.SafeCall(Common.PARENT, delegate
            {
                //Empty the notification system
                NotificationSys.PullAll();
                //Fetch notification 1 by 1
                for (int i = 0; i < c.GetRecordCount(1); i++)
                {
                    string[] met = c.GetValue(i, "fMeta").ToString().Split('|');
                    object[] notMet = new object[4 + met.Length];
                    notMet[0] = c.GetValue(i, "fID");
                    notMet[1] = Helper.ToMySQLDateTime(c.GetValue(i, "fStamp"));
                    notMet[2] = c.GetValue(i, "fStatus");
                    notMet[3] = c.GetValue(i, "fUID");
                    for (int k = 0; k < met.Length; k++)
                    {
                        notMet[4 + k] = met[k];
                    }
                    string imgKey =  c.GetValue(i, "fMsgType").ToString();
                    if (NotificationSys.ImageList.Images.IndexOfKey(imgKey) == -1) imgKey = "DEFAULT";
                    Notification n = new Notification(c.GetValue(i, "fMsg").ToString(),imgKey,
                        notMet);
                    NotificationSys.Push(n);
                }
                //Notify the calling code that the operation is successful.
                if(c.GetRecordCount(1) == 1)
                    sysMsg = string.Format("{0}, you have {1} notification!", CurrentUser.UID, c.GetRecordCount(1));
                else if(c.GetRecordCount(1) > 1)
                    sysMsg = string.Format("{0}, you have {1} notification(s)!", CurrentUser.UID, c.GetRecordCount(1));
                else
                    sysMsg = string.Format("{0}, you have no notifications!", CurrentUser.UID);
                hasErr = false;
                if (NotificationUpdated != null) NotificationUpdated(this, new EventArgs());
                allowLoadNotification = true;
            });
            
        }

        //Public operations
        public object[] GetMessageTypes()
        {
            return (object[])msgType.ToArray();
            
        }
        public void Push(string MessageTypeName, string msg, string[] meta)
        {
            if (hasErr) return;
            if (msgType.IndexOf(MessageTypeName) == -1)
            {
                Confirm.Error("Push Notification", "Cannot find the MessageTypeName.");
                return;
            }
            //Process meta data;
            string _meta = "";
            for (int i = 0; i < meta.Length; i++)
            {
                _meta += meta[i];
                if (i < meta.Length - 1) _meta += "|";
            }
            CodeScript s = new CodeScript("PUSH");
            s.Add(CodeIns.DBU_NEW_CORE);
            s.Add(CodeIns.DBU_CONNECT, Common.CFG.Read("DRIVER"), Common.CFG.Read("DB"),
                    Common.CFG.Read("UID"), Common.CFG.Read("PWD"), Common.CFG.Read("SERVER"));
            s.Add(CodeIns.DBU_BEGIN_TRANSACTION);
            s.Add(CodeIns.DBU_INSERT, "tblnotificationservice",
                new string[] { "fMsgType", "fMsg", "fMeta", "fStatus", "fUID" },
                new object[] { Sym.PQS(MessageTypeName.ToUpper()), Sym.PQS(msg), Sym.PQS(_meta), Sym.PQS(NotifyStatus.PUSHED.ToString()), Sym.PQS(CurrentUser.UID) });

            //Requery the Privilege and Notification data.
            s.Add(CodeIns.DBU_QUERY, "SELECT fData FROM tbllookup WHERE fKey='PRIVILEGE' ORDER BY fData ASC");
            string privsql = "";
            for (int i = 0; i < CurrentUser.PRIV.Length; i++)
            {
                privsql += "fMsgType LIKE '" + CurrentUser.PRIV[i] + "%'";
                if (i < CurrentUser.PRIV.Length - 1) privsql += " OR ";
            }
            s.Add(CodeIns.DBU_QUERY, "SELECT fMsgType, fMsg, fMeta, fStatus, fUID FROM tblnotificationservice WHERE " + privsql);
            s.Add(CodeIns.DBU_END);

            //Process Push on CoProcessor.
            CodeProcessor coProc = new CodeProcessor();
            coProc.ProcessCompleted += new EventHandler(delegate
            {
                if (coProc.State == ProcessorFlag.DataRdyErr ||
                coProc.State == ProcessorFlag.ReadyErr ||
                coProc.State == ProcessorFlag.Error)
                {
                    Confirm.Error("Cannot Push Notification", coProc.GetProcessorMessage);
                    coProc.ClearErrorFlag();
                    return;
                }
            });
            coProc.Run(s);
            
        }
        public Notification Pull(int index)
        {
            if (hasErr) return null;
            Notification n = NotificationSys.Pull(index);
            CodeScript s = new CodeScript("PULL");
            s.Add(CodeIns.DBU_NEW_CORE);
            s.Add(CodeIns.DBU_CONNECT, Common.CFG.Read("DRIVER"), Common.CFG.Read("DB"),
                    Common.CFG.Read("UID"), Common.CFG.Read("PWD"), Common.CFG.Read("SERVER"));
            s.Add(CodeIns.DBU_BEGIN_TRANSACTION);
            s.Add(CodeIns.DBU_UPDATE, "tblnotificationservice",
                new string[] { "fStatus", "fUID" },
                new object[] { Sym.PQS(NotifyStatus.PULLED.ToString()), Sym.PQS(CurrentUser.UID)},
                string.Format("(fID='{0}') AND (fStamp='{1}')", n.MetaData[0], n.MetaData[1]));
            s.Add(CodeIns.DBU_END);

            //Process Pull on CoProcessor.
            CodeProcessor coProc = new CodeProcessor(ThreadingMode.Single);
            coProc.ProcessCompleted += new EventHandler(delegate
            {
                if (coProc.State == ProcessorFlag.DataRdyErr ||
                coProc.State == ProcessorFlag.ReadyErr ||
                coProc.State == ProcessorFlag.Error)
                {
                    Confirm.Error("Cannot Pull Notification", coProc.GetProcessorMessage);
                    coProc.ClearErrorFlag();
                    hasErr = true;
                    return;
                }
            });
            coProc.Run(s);
            if (hasErr) return null;
            return n;
        }
        public bool HasError
        {
            get { return hasErr; }
        }
        public string Message
        {
            get { return sysMsg; }
        }

        //Public event
        public event EventHandler NotificationUpdated;

    }
}